package services.mes

import models.mes.{MesOrdersRepository, MesProductOrdersRec}
import play.api.Logging
import play.api.libs.json.{JsArray, JsValue, Json}
import services.kingdee.KingdeePostV2

import java.sql.Timestamp
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import javax.inject.Inject
import scala.collection.mutable
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, Future}

class MoreMesMo @Inject()(
                           moreMesPost: MoreMesPost,
                           mesRepo: MesOrdersRepository,
                           kingdeePost: KingdeePostV2
                         )(implicit exec: ExecutionContext)
  extends Logging {

  private var mesPost = moreMesPost.setConfig("")
  private var kdPost = kingdeePost.setKdAppConfig(mesPost.kdAppConfig.dCID)
  private var stepTime = 1 // 1 days

  def setStepTime(t: String) = {
    val time_str = "[DdHhMmSs]".r.findFirstIn(t).getOrElse("D").toUpperCase
    val time_num = "\\d+".r.findAllIn(t).mkString.toInt
    this.stepTime = time_str match {
      case "H" => if (time_num % 24 > 0) time_num / 24 + 1 else time_num / 24
      case "M" => if (time_num % 1440 > 0) time_num / 1440 + 1 else time_num / 1440
      case "S" => if (time_num % 86400 > 0) time_num / 86400 + 1 else time_num / 86400
      case _ => time_num
    }
    this
  }

  def setConfig(config: String) = {
    this.mesPost = moreMesPost.setConfig(config)
    this.kdPost = kingdeePost.setKdAppConfig(mesPost.kdAppConfig.dCID)
    this
  }

  private def pordUpdate(src: JsValue) = {
    val code = (src \ "BillNo").as[String]
    val date = Timestamp.valueOf("T".r.replaceAllIn((src \ "Date").as[String], " "))
    val appdate = (src \ "ApproveDate").asOpt[String].getOrElse("")
    val approveDate = if (appdate.length < 10) date else Timestamp.valueOf("T".r.replaceAllIn(appdate, " "))
    val mdate = (src \ "ModifyDate").asOpt[String].getOrElse("")
    val modifyDate = if (mdate.length < 10) approveDate else Timestamp.valueOf("T".r.replaceAllIn(mdate, " "))
    val documentStatus = (src \ "DocumentStatus").as[String]
    val fId = (src \ "Id").as[Int]
    val entity = (src \ "TreeEntity").as[JsArray].value.head
    //    logger.debug(s"pordUpdate(src: JsValue):entity=${entity.toString()}")
    val rowID = (entity \ "RowId").as[String]
    val feId = (entity \ "Id").as[Int]
    val itemId = (entity \ "MaterialId" \ "Number").as[String]
    val status = (entity \ "Status").as[String]
    val para = Json.obj(
      "FormId" -> "PRD_PPBOM",
      "FieldKeys" -> "FBillNo,FMOBillNO,FMOEntryID,FDocumentStatus,FApproveDate",
      "FilterString" -> s"FMOBillNO='$code' and FDocumentStatus ='C'",
      "OrderString" -> "", "TopRowCount" -> 0, "StartRow" -> 0, "Limit" -> 10
    )
    kdPost.executeBillQuery(para).flatMap {
      res => {
        logger.debug(s"${res.getClass}\tgetPPBom list: ${res}")
        val ppbom = Json.parse(res).as[Array[JsArray]].head
        val data = MesProductOrdersRec(
          rowid = rowID,
          mo = code,
          fentity = feId,
          mo_item = itemId,
          mo_status = documentStatus,
          mo_date = date,
          mo_modify = modifyDate,
          mo_approve = approveDate,
          status = status,
          mo_send = 0,
          ppbom = ppbom.value(0).as[String],
          ppbom_status = ppbom.value(3).as[String],
          ppbom_approve = Timestamp.valueOf("T".r.replaceAllIn(ppbom.value(4).as[String], " ")),
          ppbom_send = 0,
          mo_result = "???",
          ppbom_result = "???",
          fid = fId
        )
        mesRepo.prdUpInsert(data)
      }
    }
  }

  def getMOView(code: String) = {
    val response = kdPost.view("PRD_MO", Json.obj {
      "Number" -> code
    })
    response.flatMap { res =>
      logger.debug(s"getMOView($code):${res.take(200)}^^^^^^")
      val bool = {
        val i = res.indexOf("\"DocumentStatus\"")
        if (i >= 0 && res.length > i + 25) res.substring(i, i + 25).indexOf("\"C\"") > 0
        else false
      }
      if (bool) {
        val js = Json.parse(res)
        //        println("YYYYYYYYY")
        val result = (js \ "Result" \ "Result").as[JsValue]
        pordUpdate(src = result).flatMap {
          r => {
            if (r == 1) {
              putMO2Mes(data = result)
            }
            else {
              println("WWWWWWWWWWW");
              Future("")
            }
          }
        }
      }
      else {
        println("ZZZZZZZZZZZZZZ")
        Future(res)
      }
    }
  }

  private def changeMOInfo(src: JsValue) = {
    //    logger.debug(s"changePorductInfo(src: JsValue):${src.toString()}")
    //    val resList: mutable.ArrayBuffer[(String, String)] = mutable.ArrayBuffer()
    val result = src
    val project_no = (result \ "BillNo").as[String] //// "A202204240001",  //是 工单号
    //// Result["PrdOrgId"]["Number"]  // "FA174AFF136D496A87B65443D22357E3",
    val data_auth = "1"
    val project_type = (result \ "BillType" \ "Number").as[String] match {
      case "SCDD01_SYS" => "1" // 汇报入库-普通生产
      case "SCDD03_SYS" => "2" // 直接入库-普通生产
      case _ => "0"
    }
    val v = (result \ "TreeEntity").as[JsArray].value.head
    //        logger.debug(s"changePorductInfo(src: JsValue):v=${v.toString()}")
    val rowID = (v \ "RowId").as[String]
    val model_code = (v \ "MaterialId" \ "Number").as[String]
    val tmp_var = ((v \ "MaterialId" \ "Specification").as[JsArray].value.head \ "Value").as[String]
    val model_var = if (tmp_var.length > 1) tmp_var else "null"
    val cust_code = v("SaleOrderId").as[Long].toString
    val sales_no = v("SaleOrderNo").as[String]
    val shop_sn = (v \ "WorkShopID" \ "Number").as[String]
    val area_sn = (v \ "REMWorkShopId").asOpt[String].getOrElse("")
    val plan_de_date = v("PlanFinishDate").as[String]
    val plan_qty = v("Qty").as[Double].toInt
    val plan_start = v("PlanStartDate").as[String]
    val plan_end = v("PlanFinishDate").as[String]
    val user = result("CreatorId")("Name").as[String]
    val create = v("PlanConfirmDate").as[String]
    val fId = result("Id").as[Int]
    val feId = v("Id").as[Int]
    val gdbody =
      """{"dataList": {""" +
        s""""PROJECT_NO": "$project_no",""" + // "A202204240001",  //是 工单号
        s""""DATA_AUTH": "$data_auth",""" + // "FA174AFF136D496A87B65443D22357E3",
        s""""PROJECT_TYPE": "$project_type",""" + // 是 工单类型，1：汇报入库-普通生产2：直接入库-普通生产
        s""""MODEL_CODE": "$model_code",  """ + // "5001-00001", //是 产品料号
        s""""MODEL_VER": "$model_var",""" + // "5001-0000001", //是 产品版本
        s""""CUST_CODE": "$cust_code",""" + // "2130", // 客户编码
        s""""SALES_NO": "$sales_no",""" + // "S00000000001", // 销售单号
        s""""SHOP_SN": "$shop_sn",""" + // "A01",  // 车间
        s""""AREA_SN": "$area_sn",""" + // "A01001", //
        s""""PLAN_DELIVERY_DATE": "$plan_de_date",""" + // "2022-04-25 08:00:00", // 计划交货日期（2022-04-25）
        s""""PLAN_QTY": $plan_qty,""" + // "1000", // 是	计划数量
        s""""PLAN_START_TIME": "$plan_start",""" + // "2022-04-15 08:00:00", // 预计开工时间（2022-4-15）
        s""""PLAN_END_TIME": "$plan_end",  """ + // "2022-04-15 17:00:00", // 预计完工时间（2022-4-15）
        s""""CREATE_USER": "$user",""" + // "zhansan", // 开单人
        s""""CREATE_TIME": "$create",""" + // "2022-04-11 11:10:32" // 开单时间
        s""""ERP_SBILL_ID": $fId,""" +
        s""""ERP_S_ID": $feId""" +
        s"""}}"""
    //        resList.append((gdbody, rowID))
    //        logger.debug((gdbody, rowID, fId).toString())
    (gdbody, rowID, fId, project_no)
  }

  private def putMO2Mes(data: JsValue) = {
    val body = changeMOInfo(data)
    //    logger.debug(s"changeMOInfo(data):${putData.toString()}")
    logger.debug(s"changeMOInfo(data):body=${body.toString()}")
    mesRepo.prdFindByMo(body._2).flatMap {
      mo_db_rec =>
        if (mo_db_rec.nonEmpty && mo_db_rec.head.mo_send != 1) {
          kdPost.save(
            "PRD_MO",
            s"""{"NeedUpDateFields":["F_SEND_STATUS"],"Model":{"FID":${body._3},"F_SEND_STATUS":"F"}}"""
          ).flatMap {
            kdres => {
              logger.debug(s"${body._4} set F_SEND_STATUS=F >$kdres")
              val kdbool = {
                val i = kdres.indexOf("IsSuccess")
                if (i >= 0) kdres.substring(i, i + 20).indexOf("true") > 0
                else false
              }
              if (kdbool) {
                mesPost.saveMO(Json.parse(body._1)).flatMap {
                  res => {
                    val msg = s"mesPost.saveMO: ${body._1} => $res"
                    logger.info(msg)
                    val bool = {
                      val i = res.indexOf("result")
                      if (i >= 0) res.substring(i, i + 20).indexOf("true") > 0
                      else false
                    }
                    if (bool) {
                      mesRepo.prdMoSetSendSign(mo_db_rec.head.rowid, 1, res).flatMap {
                        r => {
                          kdPost.save(
                            "PRD_MO",
                            s"""{"NeedUpDateFields":["F_SEND_STATUS"],"Model":{"FID":${body._3},"F_SEND_STATUS":"X"}}"""
                          )
                            .map(
                              k => {
                                logger.info(s"${body._4} set F_SEND_STATUS=X >$r $k")
                                msg
                              }
                            )
                        }
                      }
                    } else {
                      mesRepo.prdMoSetSendSign(mo_db_rec.head.rowid, -1, res).map(
                        r => {
                          logger.info(s"${body._4} set F_SEND_STATUS=X >$r")
                          msg
                        }
                      )
                    }
                  }
                }
              } else {
                Future(kdres)
              }
            }
          }
        } else {
          mesPost.saveMO(Json.parse(body._1))
        }
    }
  }

  private def getOrderList(date: String) = {
    val qs = date match {
      case "date" => s"FConveyDate>=to_date('${
        LocalDate.now()
          .minusDays(stepTime)
          .format(DateTimeFormatter.ofPattern("yyyyMMdd"))
      }','yyyymmdd') AND F_SEND_STATUS <> 'Y'"
      case "status" => "FStatus in ('1','2','3')"
      case _ => s"FApproveDate>=to_date('$date','yyyymmdd') and FDocumentStatus ='C'"
    }
    logger.debug(s"getProductOrder FilterString: $qs")
    val lst: mutable.ArrayBuffer[String] = mutable.ArrayBuffer()
    var startRow = 0
    val limit = 10
    var isEnd = true
    while (isEnd) {
      val para = Json.obj(
        "FormId" -> "PRD_MO",
        "FieldKeys" -> "FBillNo,FDate,FStatus,FDocumentStatus,F_SEND_STATUS",
        // "FBillNo,FRowId,FMaterialId.fnumber,FDate,FModifyDate,FApproveDate,FStatus,FDocumentStatus,F_SEND_STATUS",
        "FilterString" -> qs,
        "OrderString" -> "",
        "TopRowCount" -> 0,
        "StartRow" -> lst.size,
        "Limit" -> limit
      )
      val res = Await.result(kdPost.executeBillQuery(para), Duration.Inf)
      logger.debug(s"${res.getClass}\tgetProductOrder list: ${res}")
      val arr = Json.parse(res).as[Array[JsArray]].map(
        f => f.value(0).as[String]
      )
      isEnd = arr.length == limit
      for (f <- arr) {
        //        getMOView(f)
        lst.append(f)
      }
    }
    //    lst.foreach(f => getMOView(f))
    //    for (f <- lst) {
    //      getMOView(f)
    //    }
    Future.sequence(
      lst.map(f => getMOView(f))
    ).map(_.flatten).map(_.toString)
  }

  private def getPPBom(mo: String = "20220417") = {
    val para = Json.obj(
      "FormId" -> "PRD_PPBOM",
      "FieldKeys" -> "FBillNo,FMOBillNO,FMOEntryID,FDocumentStatus,FApproveDate",
      //"FMOBillNO,FMOEntrySeq,FMOEntryID ,FBillNo,FApproveDate,FModifyDate,FDocumentStatus,FBOMID.fnumber,FMaterialID.fnumber",
      "FilterString" -> s"FMOBillNO='$mo' and FDocumentStatus ='C'",
      "OrderString" -> "",
      "TopRowCount" -> 0,
      "StartRow" -> 0,
      "Limit" -> 10
    )
    kdPost.executeBillQuery(para).flatMap {
      res =>
        logger.debug(s"${res.getClass}\tgetPPBom list: ${res}")
        val f = Json.parse(res).as[Array[JsArray]].head
        mesRepo.prdUpPPBomInfo(
          f.value(0).as[String],
          f.value(1).as[String],
          f.value(2).as[Int],
          f.value(3).as[String],
          Timestamp.valueOf("T".r.replaceAllIn(f.value(4).as[String], " "))
        ).map(_.toString)
    }
  }

  private def getPPBomList(mo: String) = {
    val mes = if (mo.equals("")) mesRepo.prdFindPPBom() else mesRepo.prdFindByMo(mo)
    mes.flatMap {
      ppbomLst =>
        Future.sequence(
          ppbomLst.map {
            ppbom => {
              if (ppbom.ppbom.length > 4) getPPBomView(ppbom.ppbom) //.map(f => f)
              else getPPBom(ppbom.mo)
            }
          }
        ).map(_.flatten).map(_.mkString)
    }
  }

  private def getPPBomView(code: String) = {
    kdPost.view("PRD_PPBOM", s"{\"Number\": \"$code\"}")
      .flatMap {
        res => {
          logger.debug(s"getPPBomView($code):${res.take(200)}^^^^^^")
          val bool = {
            val i = res.indexOf("\"DocumentStatus\"")
            if (i >= 0 && res.length > i + 25) res.substring(i, i + 25).indexOf("\"C\"") > 0
            else false
          }
          if (bool) {
            val js = Json.parse(res)
            val result = (js \ "Result" \ "Result").as[JsValue]
            putPPBom2Mes(data = result)
          }
          else {
            println("XXXXXXXXXXXXXX")
            Future("用料清单未审核")
          }
        }
      }
  }

  private def changePPBomInfo(src: JsValue) = {
    val data_auth = "1"
    val ppbom = (src \ "BillNo").as[String]
    val fId = (src \ "Id").as[Int]
    val moId = (src \ "MoId").as[Int]
    val fevalue = (src \ "PPBomEntry").as[JsArray].value

    case class FeFitlter(code: String, isStd: Boolean, useRate: Int, row: String)
    val fefilter = fevalue.map {
      bom => {
        FeFitlter(
          code = bom("MaterialID")("Number").as[String],
          isStd = {
            if (bom("ParentRowId").as[String].length > 10) false else true
          },
          useRate = (bom("UseRate").as[Double] * 1000).toInt,
          row = bom("RowId").as[String]
        )
      }
    }
    //    fefilter.groupBy(f => f.code).map(f => (f._1, f._2.length)).filter(f => f._2 >= 2).map(println)
    val out = mutable.ArrayBuffer[String]()
    val o = mutable.ArrayBuffer[String]()
    fefilter.sortBy(f => (f.useRate)).reverse.foreach(f => {
      if (o.contains(f.code)) out.append(f.row)
      else o.append(f.code)
    })
    //    println(o.length)
    //    println(out.mkString(","))
    //  fefilter.sortBy(f=>(f.code,f.useRate)).map(println)

    val fe = fevalue.filter(
      bom => ((bom \ "Numerator").as[Double] * 1_000_000).toLong != 0 && !out.contains(bom("RowId").as[String])
    ).map {
      bom => {
        val feId = (bom \ "Id").as[Int]
        val whcode = (bom \ "StockID" \ "Number").asOpt[String]
        val numerator = (bom \ "Numerator").as[Double]
        val denominator = bom("Denominator").as[Double]
        val useRate = bom("UseRate").as[Double]
        val qty = (numerator / denominator) * useRate / 100.0
        Json.obj(
          "ITEM_CODE" -> bom("MaterialID")("Number").as[String], //# "3001-00001",
          "ITEM_UNIT" -> bom("BaseUnitID")("Number").as[String], //# "GRAM",
          //#  "Numerator": 1.0,/#     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          //#  "Denominator": 1.0,#     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          "ITEM_QTY" -> qty,
          "ITEM_SEND_UNIT" -> bom("BaseUnitID")("Number").as[String], //# "QK",
          "ITEM_TOTAL_QTY" -> bom("MustQty").as[Double], //# 100,
          "OUT_RATE" -> 1,
          "WH_CODE" -> whcode, //# bom["StockID"],//#"1001"
          "ERP_S_ID" -> feId,
          "OwnerID" -> bom("OwnerID")("Number")
        )
      }
    }

    val gdbody = Json.obj("dataList" -> Json.obj(
      "PROJECT_NO" -> src("MOBillNO").as[String],
      "DATA_AUTH" -> data_auth,
      "ERP_SBILL_ID" -> fId,
      "FPPBomBillNo" -> ppbom,
      "ITEM_LIST" -> fe.toSeq
    ))
    (ppbom, gdbody, moId)

  }

  private def putPPBom2Mes(data: JsValue) = {
    val ppbom = changePPBomInfo(data)
    mesPost.savePPBom(ppbom._2).flatMap {
      res => {
        val msg = s"mesPost.savePPBom: ${ppbom._2} => $res"
        logger.info(msg)
        val bool = {
          val i = res.indexOf("result")
          if (i >= 0) res.substring(i, i + 20).indexOf("true") > 0
          else false
        }
        if (bool) {
          mesRepo.prdPPBomSetSendSign(ppbom._1, 1, res).flatMap {
            r => {
              kdPost.save("PRD_MO", s"""{"NeedUpDateFields":["F_SEND_STATUS"],"Model":{"FID":${ppbom._3},"F_SEND_STATUS":"Y"}}""")
                .map(
                  k => {
                    logger.info(s"${ppbom._1} set F_SEND_STATUS=Y >$r $k")
                    res
                  }
                )
            }
          }
        }
        else {
          mesRepo.prdPPBomSetSendSign(ppbom._1, -1, res).map(
            r => {
              logger.info(s"${ppbom._1} set F_SEND_STATUS=XF >$r")
              res
            }
          )
        }
      }
    }
  }

  /// public method
  def mo_order(data: String = "date") = {
    if (data.startsWith("MO")) getMOView(data)
    else getOrderList(data)
  }

  def ppbom_order(mo: String = "") = {
    getPPBomList(mo)
  }

  def runTask() = {
    getOrderList("date")
      .zip(
        getPPBomList("")
      )
      .map(_.toString)
  }
}
